package edu.pku.sei.autounit.reflect.jdt;


import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IAnnotation;
import org.eclipse.jdt.core.IMemberValuePair;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeParameter;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;

import edu.pku.sei.autounit.annotation.Context;
import edu.pku.sei.autounit.annotation.AutoUnitMethod;
import edu.pku.sei.autounit.reflect.AbstractMethod;
import edu.pku.sei.autounit.reflect.Class;
import edu.pku.sei.autounit.reflect.Type;
import edu.pku.sei.autounit.reflect.java.JavaTypeParameter;
import edu.pku.sei.autounit.util.Log;

public class JDTMethod extends AbstractMethod{

	private Type returnType;
	private Type [] parameters;
	
	private IMethod jdtMethod;
	private IType jdtType;
	private JDTTypeFactory factory;
	
	public JDTMethod(IType jdtType,Class c,IMethod jdtMethod,JDTTypeFactory factory) {

		super(c,jdtMethod.getElementName());
		this.jdtMethod = jdtMethod;
		this.jdtType = jdtType;
		this.factory = factory;
		
		try {
			this.mod = jdtMethod.getFlags();
			ITypeParameter []itp = jdtMethod.getTypeParameters();
			typeParameters = new JavaTypeParameter[itp.length];
			String[] contexts = getAnnotationContext();
			for(int i=0;i<itp.length;i++){
				typeParameters[i] = new JavaTypeParameter(itp[i].getElementName());
				if(i<contexts.length)
					typeParameters[i].setContext(factory.getType(contexts[i],jdtType,this));
				
				String []bs = itp[i].getBounds();
				Type []b = new Type[bs.length];
				for(int j=0;j<bs.length;j++){
					b[j] = factory.getType(bs[j],jdtType,this);
				}
				typeParameters[i].setBounds(b);
				
			}
		} catch (Exception e) {
			Log.gotUnexpectedExection("JDTMethod", "JDTMethod", "", e);
		}
	}
	
	private String[] getAnnotationContext(){
		IAnnotation ia = jdtMethod.getAnnotation(Context.class.getName());
		if(!ia.exists())
			ia = jdtMethod.getAnnotation(Context.class.getSimpleName());
		if(!ia.exists())
			return new String[0];
		try {
			IMemberValuePair[] mvs = ia.getMemberValuePairs();
			if(mvs.length != 0){
				Object obj = ia.getMemberValuePairs()[0].getValue();
				String[] r = new String[java.lang.reflect.Array.getLength(obj)];
				for(int i =0;i<r.length;i++){
					r[i] = (String)java.lang.reflect.Array.get(obj,i);
				}
				return r;
			}
		} catch (JavaModelException e) {
			Log.gotUnexpectedExection("JDTMethod", "getAnnotationContext", "", e);
		}
		return new String[0];
	}

	
	public Type[] getParameters() {
		if (parameters == null) {
			try {
				String[] tt = jdtMethod.getParameterTypes();
				parameters = new Type[tt.length];
				for (int i = 0; i < tt.length; i++) {
					String tn = Signature.toString(tt[i]);
					parameters[i] = factory.getType(tn, jdtType, this);
				}
			} catch (Exception e) {
				Log.gotUnexpectedExection("JDTMethod", "getParameters", "", e);
			}
		}
		return parameters;
	}

	
	public Type getReturnType() {
		if (returnType == null) {
			try {
				String rn = Signature.toString(jdtMethod.getReturnType());
				this.returnType = factory.getType(rn, jdtType, this);
			} catch (Exception e) {
				Log.gotUnexpectedExection("JDTMethod", "getParameters", "", e);
			}
		}
		return returnType;
	}

	
	public String getSource() {
		try {
			return jdtMethod.getSource();
		} catch (JavaModelException e) {
			Log.gotUnexpectedExection("JDTMethod", "getSource", "", e);
			return null;
		}
	}

	
	public java.lang.Class<?> getStrategy() {
		IAnnotation ia = jdtMethod.getAnnotation(AutoUnitMethod.class.getSimpleName());
		if(!ia.exists())
			ia = jdtMethod.getAnnotation(AutoUnitMethod.class.getName());
		if(!ia.exists())
			return null;
		else
			try {
				IMemberValuePair[] mvs = ia.getMemberValuePairs();
				if(mvs.length == 0)
					return null;
				String name = (String)ia.getMemberValuePairs()[0].getValue();
				String [][]ss = jdtType.resolveType(name);
				if(ss == null){
					Log.findBug("JDTMethod", "getStrategy", "can't resolve "+name);
					return null;
				}
				name = ss[0][0]+"."+ss[0][1];
				return java.lang.Class.forName(name);
			} catch (Exception e) {
				Log.gotUnexpectedExection("JDTMethod", "getStrategy", "", e);
				return null;
			}
	}
	
	
	public String[] getFilters() {
		// TODO Auto-generated method stub
		return null;
	}

	
	public boolean hasException() {
		try {
			return jdtMethod.getExceptionTypes().length!=0;
		} catch (JavaModelException e) {
			Log.gotUnexpectedExection("JDTMethod", "hasException", "", e);
			return false;
		}
	}
	
	
	public boolean isAbstract() {
		return Flags.isAbstract(mod);
	}

	
	public boolean isFinal() {
		return Flags.isFinal(mod);
	}

	
	public boolean isInterface() {
		return Flags.isInterface(mod);
	}

	
	public boolean isNative() {
		return Flags.isNative(mod);
	}

	
	public boolean isPrivate() {
		return Flags.isPrivate(mod);
	}

	
	public boolean isProtected() {
		return Flags.isProtected(mod);
	}

	
	public boolean isPublic() {
		return Flags.isPublic(mod);
	}

	
	public boolean isStatic() {
		return Flags.isStatic(mod);
	}

	
	public boolean isStrictfp() {
		return Flags.isStrictfp(mod);
	}

	
	public boolean isSynchronized() {
		return Flags.isSynchronized(mod);
	}

	
	public boolean isTransient() {
		return Flags.isTransient(mod);
	}

	
	public boolean isVolatile() {
		return Flags.isVolatile(mod);
	}
}
